`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/
module WalkingInversionsMemoryTester(
	clk_20mhz,
	
	mcb3_dram_dq, mcb3_dram_a, mcb3_dram_ba, mcb3_dram_cke, mcb3_dram_ras_n, mcb3_dram_cas_n, mcb3_dram_we_n,
	mcb3_dram_dm, mcb3_rzq, mcb3_dram_dqs, mcb3_dram_ck, mcb3_dram_ck_n,
	
	leds, buttons, gpio,
	uart_tx, uart_rx
    );
	 
	 ////////////////////////////////////////////////////////////////////////////////////////////////
	//IO declarations
	
	input wire clk_20mhz;
	
	//DRAM bus
	inout wire[7:0] mcb3_dram_dq;
	output wire[12:0] mcb3_dram_a;
	output wire[1:0] mcb3_dram_ba;
	output wire mcb3_dram_cke;
	output wire mcb3_dram_ras_n;
	output wire mcb3_dram_cas_n;
	output wire mcb3_dram_we_n;
	output wire mcb3_dram_dm;
	inout wire mcb3_rzq;
	output wire mcb3_dram_dqs;
	output wire mcb3_dram_ck;
	output wire mcb3_dram_ck_n;
	
	output reg[7:0] leds = 0;
	input wire[3:0] buttons;
	output reg[19:0] gpio = 0;
	
	output wire uart_tx;
	input wire uart_rx;
	
	parameter C3_SIMULATION = "FALSE";
	
	//The clock (comes from the PLL in the memory controller)
	wire clk;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Generate a nice slow debouncing clock
	reg clk_slow_edge = 0;							//asserted for one clk cycle every 2^16 cycles (roughly 1 KHz at 80 MHz)
	reg[15:0] clkdiv = 0;
	always @(posedge clk) begin
		clkdiv <= clkdiv + 1;
		clk_slow_edge <= 0;
		if(clkdiv == 0)
			clk_slow_edge <= 1;
	end
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Debounce the buttons
	
	wire[3:0] buttons_debounced;
	
	SwitchDebouncer #(.INIT_VAL(0)) btn0 (.clk(clk), .clken(clk_slow_edge), .din(buttons[0]), .dout(buttons_debounced[0]));
	SwitchDebouncer #(.INIT_VAL(0)) btn1 (.clk(clk), .clken(clk_slow_edge), .din(buttons[1]), .dout(buttons_debounced[1]));
	SwitchDebouncer #(.INIT_VAL(0)) btn2 (.clk(clk), .clken(clk_slow_edge), .din(buttons[2]), .dout(buttons_debounced[2]));
	SwitchDebouncer #(.INIT_VAL(0)) btn3 (.clk(clk), .clken(clk_slow_edge), .din(buttons[3]), .dout(buttons_debounced[3]));

	////////////////////////////////////////////////////////////////////////////////////////////////
	//MCB wrapper
	
	//Support signals
	wire calib_done;
	reg error = 0;
	
	wire fifo_full;
	
	wire start;
	assign start = buttons_debounced[0];
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// The MCB itself
	reg mcb_reset = 0;
	
	reg p0_cmd_en = 0;
	reg[2:0] p0_cmd_instr = 0;
	reg[5:0] p0_cmd_bl = 0;
	reg[29:0] p0_cmd_byte_addr = 0;
	wire p0_cmd_empty;
	wire p0_cmd_full;
	
	reg p0_wr_en = 0;
	reg[3:0] p0_wr_mask = 4'hf;	//LOW means write the bit - flipped from mask used on internal bus
	reg[31:0] p0_wr_data = 0;
	wire p0_wr_full;
	wire p0_wr_empty;
	wire[6:0] p0_wr_count;
	wire p0_wr_underrun;
	wire p0_wr_error;
	
	reg p0_rd_en = 0;
	wire[31:0] p0_rd_data;
	wire p0_rd_full;
	wire p0_rd_empty;
	wire[6:0] p0_rd_count;
	wire p0_rd_overflow;
	wire p0_rd_error;
	
	DramController #(.C3_SIMULATION(C3_SIMULATION)) mcb (
		.mcb3_dram_dq(mcb3_dram_dq), 
		.mcb3_dram_a(mcb3_dram_a), 
		.mcb3_dram_ba(mcb3_dram_ba), 
		.mcb3_dram_cke(mcb3_dram_cke), 
		.mcb3_dram_ras_n(mcb3_dram_ras_n), 
		.mcb3_dram_cas_n(mcb3_dram_cas_n), 
		.mcb3_dram_we_n(mcb3_dram_we_n), 
		.mcb3_dram_dm(mcb3_dram_dm), 
		.mcb3_rzq(mcb3_rzq), 
		.c3_sys_clk(clk_20mhz), 
		.c3_sys_rst_i(mcb_reset), 
		.c3_calib_done(calib_done), 
		//.c3_clk0(c3_clk0), 
		//.c3_rst0(c3_rst0), 
		.mcb3_dram_dqs(mcb3_dram_dqs), 
		.mcb3_dram_ck(mcb3_dram_ck), 
		.mcb3_dram_ck_n(mcb3_dram_ck_n),
		
		.sysclk_out_bufg(clk),
		
		//Port 0 - connected to internal stuff
		.c3_p0_cmd_clk(clk), 
		.c3_p0_cmd_en(p0_cmd_en), 
		.c3_p0_cmd_instr(p0_cmd_instr), 
		.c3_p0_cmd_bl(p0_cmd_bl), 
		.c3_p0_cmd_byte_addr(p0_cmd_byte_addr), 
		.c3_p0_cmd_empty(p0_cmd_empty), 
		.c3_p0_cmd_full(p0_cmd_full), 
		.c3_p0_wr_clk(clk), 
		.c3_p0_wr_en(p0_wr_en), 
		.c3_p0_wr_mask(p0_wr_mask), 
		.c3_p0_wr_data(p0_wr_data), 
		.c3_p0_wr_full(p0_wr_full), 
		.c3_p0_wr_empty(p0_wr_empty), 
		.c3_p0_wr_count(p0_wr_count), 
		.c3_p0_wr_underrun(p0_wr_underrun), 
		.c3_p0_wr_error(p0_wr_error), 
		.c3_p0_rd_clk(clk), 
		.c3_p0_rd_en(p0_rd_en), 
		.c3_p0_rd_data(p0_rd_data), 
		.c3_p0_rd_full(p0_rd_full), 
		.c3_p0_rd_empty(p0_rd_empty), 
		.c3_p0_rd_count(p0_rd_count), 
		.c3_p0_rd_overflow(p0_rd_overflow), 
		.c3_p0_rd_error(p0_rd_error)//, 
	
		);
		
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Interface logic
	
	//MCB commands from UG388
	localparam INSTR_WRITE						= 3'b000;
	localparam INSTR_READ 						= 3'b001;
	localparam INSTR_WRITE_AUTO_PRECHARGE	= 3'b010;
	localparam INSTR_READ_AUTO_PRECHARGE	= 3'b011;
	
	reg read_active = 0;
	reg[29:0] last_read_addr = 0;
	
	reg[31:0] fifo_dout = 0;
	reg doutrdy = 0;
	
	reg[3:0] state = 0;

	reg[26:0] foobar_addr = 27'h00000000;
	//we have 64MB = 0x04000000 bytes of memory.
	
	reg read_error = 0;
	reg[3:0] read_error_count = 0;
	reg[15:0] pass_count = 0;
	
	always @(posedge clk) begin

		p0_cmd_en <= 0;
		p0_cmd_instr <= 0;
		p0_cmd_bl <= 0;
		p0_cmd_byte_addr <= 0;
		
		p0_rd_en <= 0;
		
		p0_wr_en <= 0;
		p0_wr_mask <= 0;
		p0_wr_data <= 0;
		
		read_error <= 0;
		
		//save stats
		leds[7:4] <= pass_count[3:0];
		leds[3:0] <= read_error_count;
		
		//turn on one gpio led if all is well
		gpio[0] <= calib_done;
		
		////////////////////////////////////////////////////////////////
		// Memtest mode
		/*
			Walking 55AA55AA test
			
			The pattern 55AA55AA waas chosen because adjacent memory bits are inverted
			(thus crosstalk between data bits is very likely to be detected) and because
			every output switches every cycle (maximum possible SSO noise).
			
			Test operation:
				Fill memory with 55AA55AA
				For 0 to max address
					Read back, verify data = 55AA55AA
					Write 0xAA55AA55 (complement)
				For max address to 0
					Read back, verify data = AA55AA55
					Write 0x55AA55AA (complement)
		 */
		case(state) 
			
			//Wait for calibration to finish
			0: begin
				if(calib_done && start) begin
					state <= 1;
					foobar_addr <= 0;
				end
			end
			
			//Fill memory with 0x55AA55AA
			//TODO: do something more efficient than single word writes
			1: begin
				if(!p0_cmd_full && !p0_cmd_en) begin
				
					p0_cmd_en <= 1;
					p0_cmd_instr <= INSTR_WRITE;
					p0_cmd_bl <= 0;
					p0_cmd_byte_addr <= {5'h0, foobar_addr};
					p0_wr_en <= 1;
					p0_wr_mask <= 0;
					p0_wr_data <= 32'h55AA55AA;
					
					foobar_addr <= foobar_addr + 4;
					
					//64MB = 0 to 0x0400 0000.
					//Stop at the last valid address (end-4)
					if(foobar_addr == 27'h3fffffc) begin
						state <= 2;
						foobar_addr <= 0;
					end
					
				end

			end
			
			//For 0 to max address
			//		Read back, verify data = 55AA55AA
			//		Write 0xAA55AA55 (complement)
			2: begin
			
				//Wait for command bus to be free
				if(!p0_cmd_full && !p0_cmd_en) begin
					//Read back
					p0_cmd_en <= 1;
					p0_cmd_instr <= INSTR_READ;
					p0_cmd_bl <= 0;
					p0_cmd_byte_addr <= {5'h0, foobar_addr};
					state <= 3;
				end
				
			end
			
			3: begin
				//Wait for readback
				if(!p0_rd_empty) begin
					
					//Verify that the data is correct
					if(p0_rd_data == 32'h55AA55AA) begin
					end
					else begin
						read_error_count <= read_error_count + 1;
						read_error <= 1;
					end
					
					//Pop the FIFO
					p0_rd_en <= 1;
					
					//Write AA55AA55
					p0_cmd_en <= 1;
					p0_cmd_instr <= INSTR_WRITE;
					p0_cmd_bl <= 0;
					p0_cmd_byte_addr <= {5'h0, foobar_addr};
					p0_wr_en <= 1;
					p0_wr_mask <= 0;
					p0_wr_data <= 32'hAA55AA55;
					
					//If at the end, go on to the next step
					if(foobar_addr == 27'h3fffffc) begin
						state <= 4;
						//don't change address, next run is backwards
					end
					
					//Go on to the next address and prepare to read back
					else begin
						foobar_addr <= foobar_addr + 4;
						state <= 2;
					end
					
				end
			end
			
			//	For max address to 0
			//		Read back, verify data = AA55AA55
			//		Write 0x55AA55AA (complement)
			4: begin
				
				//Wait for command bus to be free
				if(!p0_cmd_full && !p0_cmd_en) begin
					//Read back
					p0_cmd_en <= 1;
					p0_cmd_instr <= INSTR_READ;
					p0_cmd_bl <= 0;
					p0_cmd_byte_addr <= {5'h0, foobar_addr};
					state <= 5;
				end
				
			end
			
			5: begin
				//Wait for readback
				if(!p0_rd_empty) begin
					
					//Verify that the data is correct
					if(p0_rd_data == 32'hAA55AA55) begin
					end
					else begin
						read_error_count <= read_error_count + 1;
						read_error <= 1;
					end
					
					//Pop the FIFO
					p0_rd_en <= 1;
					
					//Write 55AA55AA
					p0_cmd_en <= 1;
					p0_cmd_instr <= INSTR_WRITE;
					p0_cmd_bl <= 0;
					p0_cmd_byte_addr <= {5'h0, foobar_addr};
					p0_wr_en <= 1;
					p0_wr_mask <= 0;
					p0_wr_data <= 32'h55AA55AA;
					
					//If at the end, finished with the test suite
					if(foobar_addr == 0) begin
						state <= 6;
						//don't change address, next run will be forward
					end
					
					//Go on to the next address and prepare to read back
					else begin
						foobar_addr <= foobar_addr - 4;
						state <= 4;
					end
					
				end
			end
			
			//Go on to next pass
			6: begin
				foobar_addr <= 0;
				state <= 1;
				pass_count <= pass_count + 1;
			end
			
		endcase
		
	end
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	//Logic analyzer
	RedTinUARTWrapper la(
		.clk(clk), 
		.din({
				start, 				//1	127
				calib_done,			//1	126
				state,				//4	122
				p0_cmd_en,			//1	121
				p0_cmd_byte_addr,	//30	91
				p0_cmd_instr,		//3	88
				p0_cmd_bl,			//6	82
				p0_cmd_empty,		//1	81
				p0_cmd_full,		//1	80
				
				p0_rd_en,			//1	79
				p0_rd_data,			//32	47
				p0_rd_full,			//1	46
				p0_rd_empty,		//1	45
				
				pass_count,			//16	29
				read_error,			//1	28
				read_error_count,	//4	24
				
				24'b0}), 
		.uart_tx(uart_tx), 
		.uart_rx(uart_rx)
		);

endmodule
